home *** CD-ROM | disk | FTP | other *** search
/ ftp.cs.arizona.edu / ftp.cs.arizona.edu.tar / ftp.cs.arizona.edu / icon / newsgrp / group93a.txt / 000002_icon-group-sender _Sun Jan 3 21:19:20 1993.msg < prev    next >
Internet Message Format  |  1993-04-21  |  34KB

  1. Received: by cheltenham.cs.arizona.edu; Mon, 4 Jan 1993 04:34:01 MST
  2. Date: 3 Jan 93 21:19:20 GMT
  3. From: agate!netsys!pagesat!spssig.spss.com!uchinews!ellis!goer@ucbvax.Berkeley.EDU  (Richard L. Goerwitz)
  4. Organization: University of Chicago
  5. Subject: parser generator, part 3
  6. Message-Id: <1993Jan3.211920.28528@midway.uchicago.edu>
  7. References: <1993Jan3.211757.28395@midway.uchicago.edu>
  8. Sender: icon-group-request@cs.arizona.edu
  9. To: icon-group@cs.arizona.edu
  10. Status: R
  11. Errors-To: icon-group-errors@cs.arizona.edu
  12.  
  13. ---- Cut Here and feed the following to sh ----
  14. #!/bin/sh
  15. # this is ibpag.03 (part 3 of a multipart archive)
  16. # do not concatenate these parts, unpack them in order with /bin/sh
  17. # file maketbls.icn continued
  18. #
  19. if test ! -r _shar_seq_.tmp; then
  20.     echo 'Please unpack part 1 first!'
  21.     exit 1
  22. fi
  23. (read Scheck
  24.  if test "$Scheck" != 3; then
  25.     echo Please unpack part "$Scheck" next!
  26.     exit 1
  27.  else
  28.     exit 0
  29.  fi
  30. ) < _shar_seq_.tmp || exit 1
  31. if test ! -f _shar_wnt_.tmp; then
  32.     echo 'x - still skipping maketbls.icn'
  33. else
  34. echo 'x - continuing file maketbls.icn'
  35. sed 's/^X//' << 'SHAR_EOF' >> 'maketbls.icn' &&
  36. X
  37. X    *arglst[1] <= 1 | *arglst = 1 & { return arglst[1] }
  38. X    sortfield := arglst[2]        | { return sortf(arglst[1]) }
  39. X    arglst[1] := sortf(arglst[1], sortfield)
  40. X    
  41. X    old_i := 1
  42. X    every i := old_i+1 to *arglst[1] do {
  43. X        if not (arglst[1][old_i][sortfield] === arglst[1][i][sortfield])
  44. X    then {
  45. X        return sortff!(push(arglst[3:0], arglst[1][old_i : i])) |||
  46. X           sortff!(push(arglst[2:0], arglst[1][i     : 0]))
  47. X    }
  48. X    }
  49. X    return sortff!(push(arglst[3:0], arglst[1]))
  50. X
  51. Xend
  52. SHAR_EOF
  53. echo 'File maketbls.icn is complete' &&
  54. true || echo 'restore of maketbls.icn failed'
  55. rm -f _shar_wnt_.tmp
  56. fi
  57. # ============= preproc.icn ==============
  58. if test -f 'preproc.icn' -a X"$1" != X"-c"; then
  59.     echo 'x - skipping preproc.icn (File already exists)'
  60.     rm -f _shar_wnt_.tmp
  61. else
  62. > _shar_wnt_.tmp
  63. echo 'x - extracting preproc.icn (Text)'
  64. sed 's/^X//' << 'SHAR_EOF' > 'preproc.icn' &&
  65. X############################################################################
  66. X#
  67. X#    Name:     preproc.icn
  68. X#
  69. X#    Title:     file preprocessing utilities for IBPAG
  70. X#
  71. X#    Author:     Richard L. Goerwitz
  72. X#
  73. X#    Version: 1.18
  74. X#
  75. X############################################################################
  76. X#  
  77. X#      This file contains the preprocessing subsystem for IBPAG.
  78. X#  Essentially, the routines contained here read the file, find the
  79. X#  defines, do macro substitutions, find the start_symbol declaration
  80. X#  (if there is one), and find and store rule definitions, outputting
  81. X#  Icon procedures in their place.  The stored rule definitions later
  82. X#  get used by CONST_STATES, and turned into a parser.
  83. X#
  84. X############################################################################
  85. X#
  86. X#  Links: none
  87. X#
  88. X#  See also: ibpag.icn, maketbls.icn, debugme.icn, and esp. itokens.icn
  89. X#
  90. X############################################################################
  91. X
  92. X# ximage is only for debugging
  93. X# link ximage
  94. X
  95. X# declared in maketbls.icn
  96. X# global ptbl, start_symbol, line_number
  97. X
  98. Xrecord symbol(str, terminal)
  99. Xrecord rule(LHS, RHS, priority, associativity, procname)
  100. X
  101. X#
  102. X# makeptbl:  file -> table
  103. X#            (f)  -> ptbl
  104. X#
  105. X#     Where f is a file containing IBPAG source code, and ptbl is a
  106. X#     table of the productions contained in f; writes a preprocessed
  107. X#     version of f to f2 (stdout by default) as a side-effect (an
  108. X#     important one, nonetheless).  If a "start_symbol X" declaration
  109. X#     is encountered, it also sets the start-symbol to "X" (default is
  110. X#     "S").
  111. X#
  112. X#     The structure of ptbl: key = LHS (string), value = rule list
  113. X#     (i.e. list of rule records).  Keys are always nonterminals, as
  114. X#     there is no need to record terminals (they appear only in the
  115. X#     RHS of rules).
  116. X#
  117. X#     Ptbl is global, so there really isn't any need to return it.  It
  118. X#     is used by almost every routine in maketbls.icn.
  119. X#
  120. Xprocedure makeptbl(f, f2)
  121. X
  122. X    local rulenum, state, separator, T, r, astr, new_r, RHS
  123. X    # global ptbl, start_symbol
  124. X    initial {
  125. X    start_symbol := "S"
  126. X    ptbl := table()
  127. X    }
  128. X
  129. X    rulenum := 0
  130. X    state := 0
  131. X    separator := ""
  132. X    #
  133. X    # Iparse_tokens is in itokens.icn and suspends TOK records
  134. X    # having a sym and str field.  The sym field contains symbol
  135. X    # names; the str field contains their actual string values in the
  136. X    # source file.
  137. X    #
  138. X    every T := \iparse_tokens(f) do {
  139. X
  140. X    #
  141. X    # Check for null sym field (iparse_tokens uses TOK(&null,
  142. X    # "\n") to signal the presence of a syntactically meaningless
  143. X    # newline; we want to print it later on so as to maintain the
  144. X    # original line structure), but we don't want to actually
  145. X    # parse it here.
  146. X    #
  147. X    if \T.sym then {
  148. X
  149. X        # Note that this little automaton passes its input through
  150. X        # only under certain conditions in states 0 and 8.
  151. X        # Otherwise it is either reading a rule or a start_symbol
  152. X        # definition.
  153. X        #
  154. X        case state of {
  155. X
  156. X        0       : {
  157. X            # Typical case:  We are looking for the start of
  158. X            # the next start_symbol or rule declaration.  If
  159. X            # neither is found, do nuttin' except to pass the
  160. X            # T record on to the printing routine below.
  161. X            #
  162. X            case T.sym of {
  163. X            "STARTSYM" : {
  164. X                state := 9
  165. X                next
  166. X            }
  167. X            "RULE"         : {
  168. X                r  := rule(,,,,"_" || right(rulenum +:= 1, 5, "0"))
  169. X                state := 1
  170. X                next
  171. X            }
  172. X            default        : &null
  173. X            }
  174. X        }
  175. X
  176. X        1       : {
  177. X            # We are in a rule def.  Look for the priority
  178. X            # next.  If we don't get an INTLIT or REALLIT,
  179. X            # then assign a default priority, and see if we
  180. X            # have an IDENT.  If so, then see if it's the
  181. X            # associativity; if so, set the associativity; if
  182. X            # not, assign a default associativity and then see
  183. X            # if we have a rule LHS.  If not, then we have an
  184. X            # error.
  185. X            #
  186. X            if T.sym == ("INTLIT"|"REALLIT") then {
  187. X            r.priority := real(T.str)
  188. X            state := 2
  189. X            next
  190. X            } else if T.sym == "IDENT" then {
  191. X            # if you change the default priority here, do
  192. X            # it in maketbls.icn, too (e.g. rule(,,1))
  193. X            r.priority := 1
  194. X            if T.str == ("left"|"right"|"none")
  195. X            then {
  196. X                r.associativity := T.str
  197. X                state := 3
  198. X                next
  199. X            } else {
  200. X                r.associativity := "none" # default
  201. X                r.LHS := T.str
  202. X                state := 4
  203. X                next
  204. X            }
  205. X            } else oh_no("line "|| line_number, 11)
  206. X        }
  207. X
  208. X        2       : {
  209. X            # We have our priority; now get the associativity
  210. X            # (looks to the tokenizer like an identifier).  If
  211. X            # the identifier doesn't have a string value of
  212. X            # "right," "left," or "none," then assign a
  213. X            # default associativity, and assume that the
  214. X            # identifier is the LHS of a rule.
  215. X            #
  216. X            T.sym == "IDENT" | oh_no("line "|| line_number, 12)
  217. X            if T.str == ("left"|"right"|"none") then {
  218. X            r.associativity := T.str
  219. X            state := 3
  220. X            next
  221. X            } else {
  222. X            r.associativity := "none" # default
  223. X            r.LHS := T.str
  224. X            state := 4
  225. X            next
  226. X            } 
  227. X        }
  228. X
  229. X        3       : {
  230. X            # Now read the LHS of the rule.
  231. X            #
  232. X            if T.sym == "IDENT" then {
  233. X            r.LHS := T.str
  234. X            state := 4
  235. X            next
  236. X            } else oh_no("line "|| line_number, 1)
  237. X        }
  238. X
  239. X        4       : {
  240. X            # Now go for the RHS of the rule (which looks like
  241. X            # the argument list to an Icon procedure). 
  242. X            #
  243. X            if T.sym == "LPAREN" then {
  244. X            r.RHS := []
  245. X            state := 5
  246. X            next
  247. X            } else oh_no("line "|| line_number, 3)
  248. X        }
  249. X
  250. X        5       : {
  251. X            # We have the left parenthesis; now read the
  252. X            # arguments.  Note that the smallest argument list
  253. X            # possible is the empty string, i.e. ("").  The
  254. X            # alternation operator, |, is permitted, but has
  255. X            # different semantics than it does for Icon code.
  256. X            #
  257. X            case T.sym of {
  258. X            "IDENT"     : put(r.RHS, symbol(T.str)) &
  259. X                state := 6
  260. X            "STRINGLIT" : put(r.RHS, symbol(no_quotes(T.str), 1)) &
  261. X                state := 6
  262. X                default     : oh_no("line "|| line_number, 2)
  263. X            }
  264. X            next
  265. X        }
  266. X
  267. X        6       : {
  268. X            # We have just read an element for the RHS of a
  269. X            # rule; now we either close the current position
  270. X            # with a comma, close the entire RHS with a
  271. X            # parend, or insert an(other) alternative, via the
  272. X            # BAR, for the last element.
  273. X            #
  274. X            case T.sym of {
  275. X            "BAR"       : r.RHS[-1] := [r.RHS[-1]] &
  276. X                state := 7
  277. X            "COMMA"     : state := 5
  278. X            "RPAREN"    : {
  279. X                astr := ""
  280. X                every astr ||:= "arg" || (1 to *r.RHS) || ","
  281. X                astr := trim(astr, ',')
  282. X                write(f2, "procedure ", r.procname, "(", astr, ")")
  283. X                separator := ""
  284. X                every RHS := expand_rhs(r.RHS) do {
  285. X                new_r := copy(r)
  286. X                new_r.RHS := RHS
  287. X                /ptbl[new_r.LHS] := []
  288. X                put(ptbl[new_r.LHS], new_r)
  289. X                }
  290. X                state := 8
  291. X            }
  292. X                default     : oh_no("line "|| line_number, 10)
  293. X            }
  294. X            next
  295. X        }
  296. X
  297. X        7       : {
  298. X            # Like state 5, only for elements encountered
  299. X            # after a BAR.  These get stuffed into a list, and
  300. X            # later turned into RHSs that are identical except
  301. X            # in cases where a BAR specified alternates for a
  302. X            # given position.  See expand_rhs().
  303. X            #
  304. X            case T.sym of {
  305. X            "IDENT"     : put(r.RHS[-1], symbol(T.str)) &
  306. X                state := 6
  307. X            "STRINGLIT" : put(r.RHS[-1],
  308. X                      symbol(no_quotes(T.str), 1)) &
  309. X                state := 6
  310. X            default     : oh_no("line "|| line_number, 6)
  311. X            }
  312. X            next
  313. X        }
  314. X
  315. X        8       : {
  316. X            # We're done the rule definition.  We are again
  317. X            # passing tokens through to the printing routine
  318. X            # below.  We're still looking for an "end"
  319. X            # keyword, though.  When we get it, go back to
  320. X            # state 0.
  321. X            #
  322. X            case T.sym of {
  323. X            "RULE"      : oh_no("line "||line_number, 7)
  324. X            "PROCEDURE" : oh_no("line "||line_number, 8)
  325. X            "END"       : state := 0
  326. X            default     : &null
  327. X            }
  328. X            # Don't go for another token yet.  NO "next"!
  329. X        }
  330. X
  331. X        9       : {
  332. X            # This state is selected by a preceding
  333. X            # "START_SYMBOL" symbol.  We don't pass input
  334. X            # through (note the "next" below).  Input gets
  335. X            # passed through again when we hit state 0.
  336. X            #
  337. X            if T.sym == "IDENT" then {
  338. X            start_symbol := T.str
  339. X            state := 0
  340. X            next
  341. X            } else oh_no("line "||line_number, 4)
  342. X        }
  343. X        }
  344. X    }
  345. X
  346. X    # This is the "printing" routine mentioned above...
  347. X    #
  348. X    # NB:  Newlines that don't need to be present are signalled by
  349. X    # a null sym field.  See the procedure do_newline().  If this
  350. X    # modelled the real Icon tokenizer, such newlines would be
  351. X    # ignored.
  352. X    #
  353. X    if any(&digits ++ &letters ++'_.', \T.str, 1, 2) & \T.sym ~=="DOT"
  354. X    then writes(f2, separator)
  355. X
  356. X    writes(f2, T.str)
  357. X
  358. X    if any(&digits ++ &letters ++'_.', \T.str,-1, 0) & \T.sym ~=="DOT"
  359. X    then separator := " " else separator := ""
  360. X    }
  361. X
  362. X#   write(ximage(ptbl))
  363. X    # Ptbl is global, so this really isn't necessary.
  364. X    return ptbl
  365. X    
  366. Xend
  367. X
  368. X
  369. X#
  370. X#  no_quotes:  string -> string
  371. X#              s      -> s2
  372. X#
  373. X#      Where s is the literal value of some STRINGLIT, and s2 is that
  374. X#      same literal value, with the enclosing quotation markes
  375. X#      removed.  E.g. "\"ab\"" -> "ab".
  376. X#
  377. Xprocedure no_quotes(s)
  378. X    return s ? 2(="\"", tab(-1), ="\"")
  379. Xend
  380. X
  381. X
  382. X#
  383. X#  expand_rhs:  list -> list
  384. X#
  385. X#      Expand_rhs takes a list in which the elements are either
  386. X#      symbols or lists of symbols, and produces lists with only
  387. X#      symbols.  E.g.
  388. X#
  389. X#          [[[symbol1], symbol2], symbol3] -> [symbol1, symbol3]
  390. X#                                             [symbol2, symbol3]
  391. X#
  392. Xprocedure expand_rhs(RHS)
  393. X    *RHS = 0 & { return RHS }
  394. X    suspend [expand_elem(RHS[1])] ||| expand_rhs(RHS[2:0])
  395. Xend
  396. X#
  397. X#
  398. Xprocedure expand_elem(elem)
  399. X    if type(elem) == "symbol"
  400. X    then return elem
  401. X    else {
  402. X    suspend expand_elem(elem[1])
  403. X    suspend elem[2]
  404. X    }
  405. Xend
  406. X    
  407. SHAR_EOF
  408. true || echo 'restore of preproc.icn failed'
  409. rm -f _shar_wnt_.tmp
  410. fi
  411. # ============= itokens.icn ==============
  412. if test -f 'itokens.icn' -a X"$1" != X"-c"; then
  413.     echo 'x - skipping itokens.icn (File already exists)'
  414.     rm -f _shar_wnt_.tmp
  415. else
  416. > _shar_wnt_.tmp
  417. echo 'x - extracting itokens.icn (Text)'
  418. sed 's/^X//' << 'SHAR_EOF' > 'itokens.icn' &&
  419. X############################################################################
  420. X#
  421. X#    Name:     itokens.icn
  422. X#
  423. X#    Title:     itokens (Icon source-file tokenizer)
  424. X#
  425. X#    Author:     Richard L. Goerwitz
  426. X#
  427. X#    Version: 1.7
  428. X#
  429. X############################################################################
  430. X#  
  431. X#  This file contains a tokenizer to be used for Icon source files.
  432. X#  Normally it would be incorporated into a package utilizing IBPAG,
  433. X#  but it has a stub main procedure that makes it potentially a
  434. X#  standalone package as well.
  435. X#
  436. X############################################################################
  437. X#
  438. X#  Links:  slashupto
  439. X#
  440. X#  Requires:  coexpressions
  441. X#
  442. X############################################################################
  443. X
  444. X#link ximage
  445. X#link slashupto #make sure you have version 1.2 or above
  446. X
  447. Xglobal next_c, line_number
  448. Xrecord TOK(sym, str)
  449. X
  450. X#
  451. X#  stub main for testing
  452. X#
  453. X#procedure main()
  454. X#
  455. X#    local separator
  456. X#    separator := ""
  457. X#    every T := \iparse_tokens(&input) do {
  458. X#    #
  459. X#    # Newlines that don't need to be present are signalled by a
  460. X#    # null sym field.  See the procedure do_newline().  If this
  461. X#    # modelled the real Icon tokenizer, such newlines would be
  462. X#    # ignored, and no token (not even a dummy token) would be
  463. X#    # suspended.
  464. X#    #
  465. X#        if any(&digits ++ &letters ++ '_.', \T.str, 1, 2) & \T.sym ~== "DOT"
  466. X#        then writes(separator)
  467. X#    writes(T.str)
  468. X#    if any(&digits ++ &letters ++ '_.', \T.str, -1, 0) & \T.sym ~== "DOT"
  469. X#    then separator := " " else separator := ""
  470. X#    }
  471. X#
  472. X#end
  473. X
  474. X#
  475. X# iparse_tokens:  file     -> TOK records (a generator)
  476. X#                 (stream) -> tokens
  477. X#
  478. X#     Where file is an open input stream, and tokens are TOK records
  479. X#     holding both the token type and actual token text.
  480. X#
  481. X#     TOK records contain two parts, a preterminal symbol (the first
  482. X#     "sym" field), and the actual text of the token ("str").  The
  483. X#     parser only pays attention to the sym field, although the
  484. X#     strings themselves get pushed onto the value stack.
  485. X#
  486. X#     Note the following kludge:  Unlike real Icon tokenizers, this
  487. X#     procedure returns syntactially meaningless newlines as TOK
  488. X#     records with a null sym field.  Normally they would be ignored.
  489. X#     I wanted to return them so they could be printed on the output
  490. X#     stream, thus preserving the line structure of the original
  491. X#     file, and making later diagnostic messages more usable.
  492. X#
  493. Xprocedure iparse_tokens(stream, getchar)
  494. X
  495. X    local elem, whitespace, last_token, token, primitives, reserveds
  496. X    static be_tbl, reserved_tbl, operators
  497. X    initial {
  498. X
  499. X    #  Primitive Tokens
  500. X    #
  501. X    primitives := [
  502. X               ["identifier",      "IDENT",     "be"],
  503. X               ["integer-literal", "INTLIT",    "be"],
  504. X               ["real-literal",    "REALLIT",   "be"],
  505. X               ["string-literal",  "STRINGLIT", "be"],
  506. X               ["cset-literal",    "CSETLIT",   "be"],
  507. X               ["end-of-file",     "EOFX",      "" ]]
  508. X
  509. X    # Reserved Words
  510. X    #
  511. X    reserveds  := [
  512. X               ["break",           "BREAK",     "be"],
  513. X               ["by",              "BY",        ""  ],
  514. X               ["case",            "CASE",      "b" ],
  515. X               ["create",          "CREATE",    "b" ],
  516. X               ["default",         "DEFAULT",   "b" ],
  517. X               ["do",              "DO",        ""  ],
  518. X                       ["else",            "ELSE",      ""  ],
  519. X               ["end",             "END",       "b" ],
  520. X               ["every",           "EVERY",     "b" ],
  521. X               ["fail",            "FAIL",      "be"],
  522. X               ["global",          "GLOBAL",    ""  ],
  523. X               ["if",              "IF",        "b" ],
  524. X               ["initial",         "INITIAL",   "b" ],
  525. X               ["invocable",       "INVOCABLE", ""  ],
  526. X               ["link",            "LINK",      ""  ],
  527. X               ["local",           "LOCAL",     "b" ],
  528. X               ["next",            "NEXT",      "be"],
  529. X               ["not",             "NOT",       "b" ],
  530. X               ["of",              "OF",        ""  ],
  531. X               ["procedure",       "PROCEDURE", ""  ],
  532. X               ["record",          "RECORD",    ""  ],
  533. X               ["repeat",          "REPEAT",    "b" ],
  534. X               ["return",          "RETURN",    "be"],
  535. X               # Keyword beginning a rule definition. Like "procedure."
  536. X               ["rule",            "RULE",      ""  ],     #<-NB
  537. X               # Keyword beginning a start-symbol declaration.
  538. X               ["start_symbol",    "STARTSYM",  ""  ],
  539. X               ["static",          "STATIC",    "b" ],
  540. X               ["suspend",         "SUSPEND",   "be"],
  541. X               ["then",            "THEN",      ""  ],
  542. X               ["to",              "TO",        ""  ],
  543. X               ["until",           "UNTIL",     "b" ],
  544. X               ["while",           "WHILE",     "b" ]]
  545. X
  546. X    # Operators
  547. X    #
  548. X    operators  := [
  549. X               [":=",              "ASSIGN",    ""  ],
  550. X               ["@",               "AT",        "b" ],
  551. X               ["@:=",             "AUGACT",    ""  ],
  552. X               ["&:=",             "AUGAND",    ""  ],
  553. X               ["=:=",             "AUGEQ",     ""  ],
  554. X               ["===:=",           "AUGEQV",    ""  ],
  555. X               [">=:=",            "AUGGE",     ""  ],
  556. X               [">:=",             "AUGGT",     ""  ],
  557. X               ["<=:=",            "AUGLE",     ""  ],
  558. X               ["<:=",             "AUGLT",     ""  ],
  559. X               ["~=:=",            "AUGNE",     ""  ],
  560. X               ["~===:=",          "AUGNEQV",   ""  ],
  561. X               ["==:=",            "AUGSEQ",    ""  ],
  562. X               [">>=:=",           "AUGSGE",    ""  ],
  563. X               [">>:=",            "AUGSGT",    ""  ],
  564. X               ["<<=:=",           "AUGSLE",    ""  ],
  565. X               ["<<:=",            "AUGSLT",    ""  ],
  566. X               ["~==:=",           "AUGSNE",    ""  ],
  567. X               ["\\",              "BACKSLASH", "b" ],
  568. X               ["!",               "BANG",      "b" ],
  569. X               ["|",               "BAR",       "b" ],
  570. X               ["^",               "CARET",     "b" ],
  571. X               ["^:=",             "CARETASGN", "b" ],
  572. X               [":",               "COLON",     ""  ],
  573. X               [",",               "COMMA",     ""  ],
  574. X               ["||",              "CONCAT",    "b" ],
  575. X                       ["||:=",            "CONCATASGN",""  ],
  576. X               ["&",               "CONJUNC",   "b" ],
  577. X               [".",               "DOT",       "b" ],
  578. X               ["--",              "DIFF",      "b" ],
  579. X               ["--:=",            "DIFFASGN",  ""  ],
  580. X               ["===",             "EQUIV",     "b" ],
  581. X               ["**",              "INTER",     "b" ],
  582. X               ["**:=",            "INTERASGN", ""  ],
  583. X               ["{",               "LBRACE",    "b" ],
  584. X               ["[",               "LBRACK",    "b" ],
  585. X               ["|||",             "LCONCAT",   "b" ],
  586. X               ["|||:=",           "LCONCATASGN","" ],
  587. X               ["==",              "LEXEQ",     "b" ],
  588. X               [">>=",             "LEXGE",     ""  ],
  589. X               [">>",              "LEXGT",     ""  ],
  590. X               ["<<=",             "LEXLE",     ""  ],
  591. X               ["<<",              "LEXLT",     ""  ],
  592. X               ["~==",             "LEXNE",     "b" ],
  593. X               ["(",               "LPAREN",    "b" ],
  594. X               ["-:",              "MCOLON",    ""  ],
  595. X               ["-",               "MINUS",     "b" ],
  596. X               ["-:=",             "MINUSASGN", ""  ],
  597. X               ["%",               "MOD",       ""  ],
  598. X               ["%:=",             "MODASGN",   ""  ],
  599. X               ["~===",            "NOTEQUIV",  "b" ],
  600. X               ["=",               "NUMEQ",     "b" ],
  601. X               [">=",              "NUMGE",     ""  ],
  602. X               [">",               "NUMGT",     ""  ],
  603. X               ["<=",              "NUMLE",     ""  ],
  604. X               ["<",               "NUMLT",     ""  ],
  605. X               ["~=",              "NUMNE",     "b" ],
  606. X               ["+:",              "PCOLON",    ""  ],
  607. X               ["+",               "PLUS",      "b" ],
  608. X               ["+:=",             "PLUSASGN",  ""  ],
  609. X               ["?",               "QMARK",     "b" ],
  610. X               ["<-",              "REVASSIGN", ""  ],
  611. X               ["<->",             "REVSWAP",   ""  ],
  612. X               ["}",               "RBRACE",    "e" ],
  613. X               ["]",               "RBRACK",    "e" ],
  614. X               [")",               "RPAREN",    "e" ],
  615. X               [";",               "SEMICOL",   ""  ],
  616. X               ["?:=",             "SCANASGN",  ""  ],
  617. X               ["/",               "SLASH",     "b" ],
  618. X               ["/:=",             "SLASHASGN", ""  ],
  619. X               ["*",               "STAR",      "b" ],
  620. X               ["*:=",             "STARASGN",  ""  ],
  621. X               [":=:",             "SWAP",      ""  ],
  622. X               ["~",               "TILDE",     "b" ],
  623. X               ["++",              "UNION",     "b" ],
  624. X               ["++:=",            "UNIONASGN", ""  ],
  625. X               ["$(",              "LBRACE",    "b" ],
  626. X               ["$)",              "RBRACE",    "e" ],
  627. X               ["$<",              "LBRACK",    "b" ],
  628. X               ["$>",              "RBRACK",    "e" ]]
  629. X
  630. X    # static be_tbl, reserved_tbl
  631. X    reserved_tbl := table()
  632. X    every elem := !reserveds do
  633. X        insert(reserved_tbl, elem[1], elem[2])
  634. X    be_tbl := table()
  635. X    every elem := !primitives | !reserveds | !operators do {
  636. X        insert(be_tbl, elem[2], elem[3])
  637. X    }
  638. X    }
  639. X
  640. X    /getchar   := create {
  641. X    line_number := 0
  642. X    ! ( 1(!stream, line_number +:=1) || "\n" )
  643. X    }
  644. X    whitespace := ' \t'
  645. X    /next_c    := @getchar
  646. X
  647. X    repeat {
  648. X    case next_c of {
  649. X
  650. X        "."      : {
  651. X        # Could be a real literal *or* a dot operator.  Check
  652. X        # following character to see if it's a digit.  If so,
  653. X        # it's a real literal.  We can only get away with
  654. X        # doing the dot here because it is not a substring of
  655. X        # any longer identifier.  If this gets changed, we'll
  656. X        # have to move this code into do_operator().
  657. X        #
  658. X        last_token := do_dot(getchar)
  659. X        suspend last_token
  660. X#        write(&errout, "next_c == ", image(next_c))
  661. X        next
  662. X        }
  663. X
  664. X        "\n"     : {
  665. X        # If do_newline fails, it means we're at the end of
  666. X        # the input stream, and we should break out of the
  667. X        # repeat loop.
  668. X        #
  669. X        every last_token := do_newline(getchar, last_token, be_tbl)
  670. X        do suspend last_token
  671. X        if next_c === &null then break
  672. X        next
  673. X        }
  674. X
  675. X        "\#"     : {
  676. X        # Just a comment.  Strip it by reading every character
  677. X        # up to the next newline.  The global var next_c
  678. X        # should *always* == "\n" when this is done.
  679. X        #
  680. X        do_number_sign(getchar)
  681. X#        write(&errout, "next_c == ", image(next_c))
  682. X        next
  683. X        }
  684. X
  685. X        "\""    : {
  686. X        # Suspend as STRINGLIT everything from here up to the
  687. X        # next non-backslashed quotation mark, inclusive
  688. X        # (accounting for the _ line-continuation convention).
  689. X        #
  690. X        last_token := do_quotation_mark(getchar)
  691. X        suspend last_token
  692. X#        write(&errout, "next_c == ", image(next_c))
  693. X        next
  694. X        }
  695. X
  696. X        "'"     : {
  697. X        # Suspend as CSETLIT everything from here up to the
  698. X        # next non-backslashed apostrophe, inclusive.
  699. X        #
  700. X        last_token := do_apostrophe(getchar)
  701. X        suspend last_token
  702. X#        write(&errout, "next_c == ", image(next_c))
  703. X        next
  704. X        }
  705. X
  706. X        &null   : oh_no(&null, 5) # unexpected EOF message
  707. X
  708. X        default : {
  709. X        # If we get to here, we have either whitespace, an
  710. X        # integer or real literal, an identifier or reserved
  711. X        # word (both get handled by do_identifier), or an
  712. X        # operator.  The question of which we have can be
  713. X        # determined by checking the first character.
  714. X        #
  715. X        if any(whitespace, next_c) then {
  716. X            # Like all of the TOK forming procedures,
  717. X            # do_whitespace resets next_c.
  718. X            do_whitespace(getchar, whitespace)
  719. X            # don't suspend any tokens
  720. X            next
  721. X        }
  722. X        if any(&digits, next_c) then {
  723. X            last_token := do_digits(getchar)
  724. X            suspend last_token
  725. X            next
  726. X        }
  727. X        if any(&letters ++ '_', next_c) then {
  728. X            last_token := do_identifier(getchar, reserved_tbl)
  729. X            suspend last_token
  730. X            next
  731. X        }
  732. X#        write(&errout, "it's an operator")
  733. X        last_token := do_operator(getchar, operators)
  734. X        suspend last_token
  735. X        next
  736. X        }
  737. X    }
  738. X    }
  739. X
  740. X    # If stream argument is nonnull, then we are in the top-level
  741. X    # iparse_tokens().  If not, then we are in a recursive call, and
  742. X    # we should not emit all this end-of-file crap.
  743. X    #
  744. X    if \stream then {
  745. X    suspend TOK("EOFX")
  746. X    return TOK("$")
  747. X    }
  748. X    else fail
  749. X
  750. Xend
  751. X
  752. X
  753. X#
  754. X#  do_dot:  coexpression -> TOK record
  755. X#           getchar      -> t
  756. X#
  757. X#      Where getchar is the coexpression that produces the next
  758. X#      character from the input stream and t is a token record whose
  759. X#      sym field contains either "REALLIT" or "DOT".  Essentially,
  760. X#      do_dot checks the next char on the input stream to see if it's
  761. X#      an integer.  Since the preceding char was a dot, an integer
  762. X#      tips us off that we have a real literal.  Otherwise, it's just
  763. X#      a dot operator.  Note that do_dot resets next_c for the next
  764. X#      cycle through the main case loop in the calling procedure.
  765. X#
  766. Xprocedure do_dot(getchar)
  767. X
  768. X    local token
  769. X    # global next_c
  770. X
  771. X#    write(&errout, "it's a dot")
  772. X
  773. X    # If dot's followed by a digit, then we have a real literal.
  774. X    #
  775. X    if any(&digits, next_c := @getchar) then {
  776. X#    write(&errout, "dot -> it's a real literal")
  777. X    token := "." || next_c
  778. X    while any(&digits, next_c := @getchar) do
  779. X        token ||:= next_c
  780. X    if token ||:= (next_c == ("e"|"E")) then {
  781. X        while (next_c := @getchar) == "0"
  782. X        while any(&digits, next_c) do {
  783. X        token ||:= next_c
  784. X        next_c = @getchar
  785. X        }
  786. X    }
  787. X    return TOK("REALLIT", token)
  788. X    }
  789. X
  790. X    # Dot not followed by an integer; so we just have a dot operator,
  791. X    # and not a real literal.
  792. X    #
  793. X#    write(&errout, "dot -> just a plain dot")
  794. X    return TOK("DOT", ".")
  795. X    
  796. Xend
  797. X
  798. X
  799. X#
  800. X#  do_newline:  coexpression x TOK record x table -> TOK records
  801. X#               (getchar, last_token, be_tbl)     -> Ts (a generator)
  802. X#
  803. X#      Where getchar is the coexpression that returns the next
  804. X#      character from the input stream, last_token is the last TOK
  805. X#      record suspended by the calling procedure, be_tbl is a table of
  806. X#      tokens and their "beginner/ender" status, and Ts are TOK
  807. X#      records.  Note that do_newline resets next_c.  Do_newline is a
  808. X#      mess.  What it does is check the last token suspended by the
  809. X#      calling procedure to see if it was a beginner or ender.  It
  810. X#      then gets the next token by calling iparse_tokens again.  If
  811. X#      the next token is a beginner and the last token is an ender,
  812. X#      then we have to suspend a SEMICOL token.  In either event, both
  813. X#      the last and next token are suspended.
  814. X#
  815. Xprocedure do_newline(getchar, last_token, be_tbl)
  816. X
  817. X    local next_token
  818. X    # global next_c
  819. X
  820. X#    write(&errout, "it's a newline")
  821. X
  822. X    # Go past any additional newlines.
  823. X    #
  824. X    while next_c == "\n" do {
  825. X        # NL can be the last char in the getchar stream; if it *is*,
  826. X    # then signal that it's time to break out of the repeat loop
  827. X    # in the calling procedure.
  828. X    #
  829. X    next_c := @getchar | {
  830. X        next_c := &null
  831. X        fail
  832. X    }
  833. X    suspend TOK(&null, next_c == "\n")
  834. X    }
  835. X
  836. X    # If there was a last token (i.e. if a newline wasn't the first
  837. X    # character of significance in the input stream), then check to
  838. X    # see if it was an ender.  If so, then check to see if the next
  839. X    # token is a beginner.  If so, then suspend a TOK("SEMICOL")
  840. X    # record before suspending the next token.
  841. X    #
  842. X    if find("e", be_tbl[(\last_token).sym]) then {
  843. X#    write(&errout, "calling iparse_tokens via do_newline")
  844. X#    &trace := -1
  845. X    # First arg to iparse_tokens can be null here.
  846. X    if next_token := iparse_tokens(&null, getchar)
  847. X    then {
  848. X#        write(&errout, "call of iparse_tokens via do_newline yields ",
  849. X#          ximage(next_token))
  850. X        if find("b", be_tbl[next_token.sym])
  851. X        then suspend TOK("SEMICOL", "\n")
  852. X        #
  853. X        # See below.  If this were like the real Icon parser,
  854. X        # the following line would be commented out.
  855. X        #
  856. X        else suspend TOK(&null, "\n")
  857. X        return next_token
  858. X    }
  859. X    else {
  860. X        #
  861. X        # If this were a *real* Icon tokenizer, it would not emit
  862. X        # any record here, but would simply fail.  Instead, we'll
  863. X        # emit a dummy record with a null sym field.
  864. X        #
  865. X        return TOK(&null, "\n")
  866. X#           &trace := 0
  867. X#        fail
  868. X    }
  869. X    }
  870. X
  871. X    # See above.  Again, if this were like Icon's own tokenizer, we
  872. X    # would just fail here, and not return any TOK record.
  873. X    #
  874. X#   &trace := 0
  875. X    return TOK(&null, "\n")
  876. X#   fail
  877. X
  878. Xend
  879. X
  880. X
  881. X#
  882. X#  do_number_sign:  coexpression -> &null
  883. X#                   getchar      -> 
  884. X#
  885. X#      Where getchar is the coexpression that pops characters off the
  886. X#      main input stream.  Sets the global variable next_c.  This
  887. X#      procedure simply reads characters until it gets a newline, then
  888. X#      returns with next_c == "\n".  Since the starting character was
  889. X#      a number sign, this has the effect of stripping comments.
  890. X#
  891. Xprocedure do_number_sign(getchar)
  892. X
  893. X    # global next_c
  894. X
  895. X#    write(&errout, "it's a number sign")
  896. X    while next_c ~== "\n" do {
  897. X    next_c := @getchar
  898. X    }
  899. X
  900. X    # Return to calling procedure to cycle around again with the new
  901. X    # next_c already set.  Next_c should always be "\n" at this point.
  902. X    return
  903. X
  904. Xend
  905. X
  906. X
  907. X#
  908. X#  do_quotation_mark:  coexpression -> TOK record
  909. X#                      getchar      -> t
  910. X#
  911. X#      Where getchar is the coexpression that yields another character
  912. X#      from the input stream, and t is a TOK record with "STRINGLIT"
  913. X#      as its sym field.  Puts everything upto and including the next
  914. X#      non-backslashed quotation mark into the str field.  Handles the
  915. X#      underscore continuation convention.
  916. X#
  917. Xprocedure do_quotation_mark(getchar)
  918. X
  919. X    local token
  920. X    # global next_c
  921. X
  922. X    # write(&errout, "it's a string literal")
  923. X    token := "\""
  924. X    while next_c := @getchar do {
  925. X    if next_c == "\n" & token[-1] == "_" then {
  926. X        token := token[1:-1]
  927. X        next
  928. X    } else {
  929. X        if slashupto("\"", token ||:= next_c, 2)
  930. X        then {
  931. X        next_c := @getchar
  932. X        # resume outermost (repeat) loop in calling procedure,
  933. X        # with the new (here explicitly set) next_c
  934. X        return TOK("STRINGLIT", token)
  935. X        }
  936. X    }
  937. X    }
  938. X
  939. Xend
  940. X
  941. X
  942. X#
  943. X#  do_apostrophe:  coexpression -> TOK record
  944. X#                  getchar      -> t
  945. X#
  946. X#      Where getchar is the coexpression that yields another character
  947. X#      from the input stream, and t is a TOK record with "CSETLIT"
  948. X#      as its sym field.  Puts everything upto and including the next
  949. X#      non-backslashed apostrope into the str field.
  950. X#
  951. Xprocedure do_apostrophe(getchar)
  952. X
  953. X    local token
  954. X    # global next_c
  955. X
  956. X#   write(&errout, "it's a cset literal")
  957. X    token := "'"
  958. X    while next_c := @getchar do {
  959. X    if slashupto("'", token ||:= next_c, 2)
  960. X    then {
  961. X        next_c := @getchar
  962. X        # Return & resume outermost containing loop in calling
  963. X        # procedure w/ new next_c.
  964. X        return TOK("CSETLIT", token)
  965. X    }
  966. X    }
  967. X
  968. Xend
  969. X
  970. X
  971. X#
  972. X#  do_digits:  coexpression -> TOK record
  973. X#              getchar      -> t
  974. X#
  975. X#      Where getchar is the coexpression that produces the next char
  976. X#      on the input stream, and where t is a TOK record containing
  977. X#      either "REALLIT" or "INTLIT" in its sym field, and the text of
  978. X#      the numeric literal in its str field.
  979. X#
  980. Xprocedure do_digits(getchar)
  981. X
  982. X    local token, tok_record
  983. X    # global next_c
  984. X
  985. X    # Assume integer literal until proven otherwise....
  986. X    tok_record := TOK("INTLIT")
  987. X
  988. X#   write(&errout, "it's an integer or real literal")
  989. X    token := ("0" ~== next_c) | ""
  990. X    while any(&digits, next_c := @getchar) do
  991. X    token ||:= next_c
  992. X    if token ||:= (next_c == ("R"|"r")) then {
  993. X    while any(&digits, next_c := @getchar) do
  994. X        token ||:= next_c
  995. X    } else {
  996. X    if token ||:= (next_c == ".") then {
  997. X        while any(&digits, next_c := @getchar) do
  998. X        token ||:= next_c
  999. X        tok_record := TOK("REALLIT")
  1000. X    }
  1001. X    if token ||:= (next_c == ("e"|"E")) then {
  1002. X        while any(&digits, next_c := @getchar) do
  1003. X        token ||:= next_c
  1004. X        tok_record := TOK("REALLIT")
  1005. X    }
  1006. X    }
  1007. X    tok_record.str := ("" ~== token) | 0
  1008. X    return tok_record
  1009. X    
  1010. Xend
  1011. X
  1012. X
  1013. X#
  1014. X#  do_whitespace:  coexpression x cset  -> &null
  1015. X#                  getchar x whitespace -> &null
  1016. X#
  1017. X#      Where getchar is the coexpression producing the next char on
  1018. X#      the input stream.  Do_whitespace just repeats until it finds a
  1019. SHAR_EOF
  1020. true || echo 'restore of itokens.icn failed'
  1021. fi
  1022. echo 'End of  part 3'
  1023. echo 'File itokens.icn is continued in part 4'
  1024. echo 4 > _shar_seq_.tmp
  1025. exit 0
  1026. -- 
  1027.  
  1028.    -Richard L. Goerwitz              goer%midway@uchicago.bitnet
  1029.    goer@midway.uchicago.edu          rutgers!oddjob!ellis!goer
  1030.